import { CaretDown, Delete } from "@ct0/icons";
import { SelectOption, SelectOptions } from "../type";
import { Type } from "./enum";
import classNames from "classnames";
import Dropdown from "rc-dropdown";
import Label from "./Label";
import Overlay from "./Overlay";
import type { CSSProperties } from "react";
import React, { useEffect, useState } from "react";
import type { SelectInfo } from "rc-menu/lib/interface";

type Value = Array<string | number>;

interface Props {
  type?: Type;
  value: Value;
  options?: SelectOptions;
  disabled?: boolean;
  placeholder?: string;
  search?: boolean;
  className?: string;
  style?: CSSProperties;
  allowClear?: boolean;
  invalid?: boolean;
  allowClearAll?: boolean;
  allowSelectAll?: boolean;
  onChange?: (v: Array<string | number>) => void;
  onInputChange?: (v: string, selectedKeys: Array<string | number>) => void;
  dropdownRender?: (originNode: React.ReactNode) => React.ReactNode;
  disabledValueOptions?: Array<{ value: string | number; label: string }>;
  notFoundContent?: React.ReactNode;
}

export interface SelectedOptionsProps extends SelectOption {
  readonly?: boolean;
}

export default function DropdownComponent({
  value: selectedKeys,
  options = [],
  placeholder,
  search,
  type,
  disabled,
  className,
  style,
  allowClear,
  invalid,
  allowClearAll,
  allowSelectAll,
  onChange,
  onInputChange,
  dropdownRender,
  disabledValueOptions = [],
  notFoundContent,
}: Props) {
  const [visible, setVisible] = useState<boolean>(false);
  const [inputVal, setInputVal] = useState("");
  const [selectedOptions, setSelectedOptions] = useState<
    SelectedOptionsProps[]
  >([]);
  useEffect(() => {
    if (selectedKeys) {
      const preOptions = selectedOptions;
      const optionsObj: Record<string | number, string> = options.reduce(
        (prev, cur) => ({ ...prev, [cur.value]: cur.label }),
        {}
      );
      const disabledOptionsObj: Record<string | number, string> =
        disabledValueOptions.reduce(
          (prev, cur) => ({ ...prev, [cur.value]: cur.label }),
          {}
        );
      const selOptions = selectedKeys.map((x) => {
        if (disabledOptionsObj[x]) {
          return { value: x, label: disabledOptionsObj[x], readonly: true };
        }
        if (optionsObj[x]) {
          const readonly = options?.find((i) => i.value === x)?.disabled;
          return { value: x, label: optionsObj[x], readonly };
        }
        const preOption = preOptions.find((y) => y.value === x);
        if (preOption) {
          return preOption;
        }
        return { value: x, label: x.toString() };
      });
      setSelectedOptions(selOptions);
    } else {
      setSelectedOptions([]);
    }
  }, [options, selectedKeys]);

  useEffect(() => {
    // 收起下拉框时清空搜索内容
    if (!visible) {
      setInputVal("");
      onInputChange?.("", selectedKeys);
    }
  }, [visible]);

  const onInputValChange = (e: any) => {
    const val = String(e.target.value).trim();
    setInputVal(val);
    onInputChange?.(val, selectedKeys);
  };

  const onClearAll = (e?: any) => {
    e?.stopPropagation();
    let keys: Array<string | number> = [],
      options: SelectedOptionsProps[] = [];
    selectedKeys.forEach((x) => {
      const disabledIdx = disabledValueOptions?.findIndex((y) => y.value === x);
      if (disabledIdx > -1) {
        keys.push(x);
        options.push({ ...disabledValueOptions[disabledIdx], readonly: true });
      }
    });
    onChange?.(keys);
    setSelectedOptions(options);
    setVisible(false);
  };

  // 只有多选类型有全选
  const onSelectAll = () => {
    // 如果没有全选(全选为选择所有非disabled类型的选项)，则为全选
    const allOption = options?.filter((option) => !option?.disabled);
    const allOptionalKeys = allOption.map((x) => x.value);
    const all = Array.from(new Set([...selectedKeys, ...allOptionalKeys]));
    if (selectedKeys.length === all.length) return;
    onChange?.(all);
    const disabledOptions = disabledValueOptions?.map((y) => ({
      ...y,
      readonly: true,
    }));
    setSelectedOptions([...allOption, ...disabledOptions]);
    setVisible(false);
  };

  const updateSelectedKeys = (
    option: SelectOption,
    callback?: (newKeys: Value, newOptions: SelectOptions) => void
  ) => {
    const key = option.value;
    if (type === Type.SINGLE_CHOICE) {
      const keys = [key];
      callback?.(keys, [option]);
      return;
    }
    const keys = [...selectedKeys];
    let selOptions = [...selectedOptions];
    const includedKeyIdx = keys.findIndex((o) => o === key);
    if (includedKeyIdx > -1) {
      keys.splice(includedKeyIdx, 1);
      selOptions = selectedOptions.filter((x) => x.value !== key);
    } else {
      keys.push(key);
      selOptions.push(option);
    }
    callback?.(keys, selOptions);
  };

  const onSelect = (value: SelectInfo) => {
    const option = options.find((o) => o?.value == value.key);
    if (!option) {
      return;
    }
    updateSelectedKeys(option, (newKeys, selOptions) => {
      onChange?.(newKeys);
      setSelectedOptions(selOptions);
      if (type == Type.SINGLE_CHOICE) setVisible(false);
    });
  };

  const onRemove = (e: any, idx: number) => {
    e.stopPropagation();
    const keys = [...selectedKeys];
    keys.splice(idx, 1);
    onChange?.(keys);

    const selOptions = [...selectedOptions];
    selOptions.splice(idx, 1);
    setSelectedOptions(selOptions);
  };

  if (disabled) {
    return (
      <div
        className={classNames(
          "ct0-select-dropdown-trigger",
          className,
          "disabled"
        )}
        role="disabled"
        style={style}
      >
        {selectedOptions.length > 0 ? (
          <Label
            onRemove={onRemove}
            disabled
            selectedOptions={selectedOptions}
            type={type}
          />
        ) : (
          placeholder
        )}
      </div>
    );
  }

  const overlay = (
    <Overlay
      search={search}
      inputVal={inputVal}
      onSelect={onSelect}
      clearAll={onClearAll}
      selectAll={onSelectAll}
      onInputValChange={onInputValChange}
      selectedKeys={selectedKeys.map((i) => String(i))}
      options={options}
      allowClearAll={allowClearAll}
      allowSelectAll={allowSelectAll}
      notFoundContent={notFoundContent}
    />
  );

  return (
    <Dropdown
      prefixCls="ct0-select"
      overlay={
        <div className="ct0-select-dropdown-overlay">
          {dropdownRender ? dropdownRender(overlay) : overlay}
        </div>
      }
      trigger="click"
      onVisibleChange={setVisible}
      visible={visible}
    >
      <div
        className={classNames("ct0-select-dropdown-trigger", className, {
          "red-border": invalid,
        })}
        style={style}
        role="trigger"
      >
        <Label
          onRemove={onRemove}
          placeholder={placeholder}
          selectedOptions={selectedOptions}
          type={type}
        />
        {selectedKeys && allowClear ? (
          <span className="delete-icon" onClick={onClearAll} role="clear-all">
            <Delete />
          </span>
        ) : (
          <span className="arrow-icon">
            <CaretDown />
          </span>
        )}
      </div>
    </Dropdown>
  );
}
